package com.cardone.common.security.shiro.web.filter.authc;

import com.cardone.common.util.*;
import com.cardone.context.DictionaryException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.SavedRequest;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Writer;
import java.util.Map;

/**
 * Created by Administrator on 2014/11/7.
 */
@Slf4j
public class CardOneFormAuthenticationFilter extends FormAuthenticationFilter {
    @Setter
    @Getter
    private boolean json = false;
    @Setter
    @Getter
    private String validationCodeParam = "KAPTCHA_SESSION_KEY";

    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        if (!json) {
            return super.executeLogin(request, response);
        }

        AuthenticationToken token = null;

        try {
            token = createToken(request, response);

            if (token == null) {
                String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
                        "must be created in order to execute a login attempt.";
                throw new IllegalStateException(msg);
            }

            Subject subject = getSubject(request, response);
            subject.login(token);
            return onLoginSuccess(token, subject, request, response);
        } catch (AuthenticationException e) {
            return onLoginFailure(token, e, request, response);
        }
    }

    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
                                     ServletRequest request, ServletResponse response) throws Exception {
        SecurityUtils.getSubject().getSession().setAttribute("isLoginError", null);

        if (!json) {
            return super.onLoginSuccess(token, subject, request, response);
        }

        response.setCharacterEncoding(CharEncoding.UTF_8);
        response.setContentType("application/json");

        String successUrl;

        SavedRequest savedRequest = org.apache.shiro.web.util.WebUtils.getAndClearSavedRequest(request);

        if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase(AccessControlFilter.GET_METHOD)) {
            successUrl = savedRequest.getRequestUrl();
        } else {
            successUrl = this.getSuccessUrl();
        }

        try (Writer out = response.getWriter()) {
            out.write(MapperUtils.toJson(ReturnDataUtils.newMap(successUrl)));

            out.flush();
        } catch (java.io.IOException ex) {
            log.error(ex.getMessage(), ex);
        }

        return false;
    }

    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
                                     ServletRequest request, ServletResponse response) {
        SecurityUtils.getSubject().getSession().setAttribute("isLoginError", true);

        if (!json) {
            return super.onLoginFailure(token, e, request, response);
        }

        response.setCharacterEncoding(CharEncoding.UTF_8);
        response.setContentType("application/json");

        Map<String, Object> map;

        if ((e instanceof UnknownAccountException) || e instanceof IncorrectCredentialsException) {
            map = ReturnDataUtils.newErrorsMap(new DictionaryException("账号不存在或密码错误!"));
        } else if (e instanceof LockedAccountException) {
            map = ReturnDataUtils.newErrorsMap(new DictionaryException("账号被锁定!"));
        } else {
            map = ReturnDataUtils.newErrorsMap(e);
        }

        try (Writer out = response.getWriter()) {
            out.write(MapperUtils.toJson(map));

            out.flush();
        } catch (java.io.IOException ex) {
            log.error(ex.getMessage(), ex);
        }

        return false;
    }

    protected AuthenticationToken createToken(String username, String password,
                                              ServletRequest request, ServletResponse response) {
        final Boolean isLoginError = (Boolean) SecurityUtils.getSubject().getSession().getAttribute("isLoginError");

        Map<String, Object> parameterMap = null;

        if (BooleanUtils.isTrue(isLoginError)) {
            parameterMap = WebUtils.getParameterMap(request);

            String validationCode = MapUtils.getString(parameterMap, "validationCode");

            if (org.apache.commons.lang3.StringUtils.isBlank(validationCode)) {
                throw new AuthenticationException("验证码不能为空");
            }

            final String capText = (String) SecurityUtils.getSubject().getSession().getAttribute(this.validationCodeParam);

            if (!org.apache.commons.lang3.StringUtils.equals(validationCode, capText)) {
                throw new AuthenticationException("验证码输入不正确");
            }
        }

        if (!json) {
            return super.createToken(username, password, request, response);
        }

        if (parameterMap == null) {
            parameterMap = WebUtils.getParameterMap(request);
        }

        username = MapUtils.getString(parameterMap, this.getUsernameParam());

        password = MapUtils.getString(parameterMap, this.getPasswordParam());

        boolean rememberMe = BooleanUtils.toBoolean(MapUtils.getString(parameterMap, this.getRememberMeParam()));

        String host = getHost(request);

        return createToken(username, password, rememberMe, host);
    }
}
